package com.example.gitclient.git;

import com.example.gitclient.model.Branch;
import com.example.gitclient.model.Commit;
import com.example.gitclient.model.GitFile;
import com.example.gitclient.model.Repository;

import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ListBranchCommand;
import org.eclipse.jgit.api.Status;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * Git操作管理类，封装JGit库的操作
 */
public class GitManager {
    private final Git git;
    private final Repository repository;

    /**
     * 打开一个已存在的Git仓库
     * @param repository 仓库信息
     * @throws IOException 如果仓库打开失败
     */
    public GitManager(Repository repository) throws IOException {
        this.repository = repository;
        git = Git.open(repository.getDirectory());
        updateCurrentBranch();
    }

    /**
     * 关闭Git仓库
     */
    public void close() {
        if (git != null) {
            git.close();
        }
    }

    /**
     * 克隆远程仓库
     * @param url 远程仓库URL
     * @param directory 本地目录
     * @param username 用户名（可为null）
     * @param password 密码（可为null）
     * @return 新的Repository对象
     * @throws GitAPIException 如果克隆失败
     */
    public static Repository cloneRepository(String url, File directory, String username, String password) throws GitAPIException {
        CredentialsProvider credentialsProvider = null;
        if (username != null && password != null) {
            credentialsProvider = new UsernamePasswordCredentialsProvider(username, password);
        }

        Git.cloneRepository()
                .setURI(url)
                .setDirectory(directory)
                .setCredentialsProvider(credentialsProvider)
                .call();

        return new Repository(directory.getName(), directory.getAbsolutePath(), url);
    }

    /**
     * 初始化新的Git仓库
     * @param directory 仓库目录
     * @return 新的Repository对象
     * @throws GitAPIException 如果初始化失败
     */
    public static Repository initRepository(File directory) throws GitAPIException {
        Git.init().setDirectory(directory).call();
        return new Repository(directory.getName(), directory.getAbsolutePath());
    }

    /**
     * 获取当前分支名称
     * @return 当前分支名称
     */
    private void updateCurrentBranch() {
        try {
            String branch = git.getRepository().getBranch();
            repository.setCurrentBranch(branch);
        } catch (IOException e) {
            repository.setCurrentBranch("unknown");
        }
    }

    /**
     * 获取所有分支
     * @return 分支列表
     * @throws GitAPIException 如果获取分支失败
     */
    public List<Branch> getBranches() throws GitAPIException {
        List<Branch> branches = new ArrayList<>();
        String currentBranch = repository.getCurrentBranch();

        // 获取本地分支
        List<Ref> localBranches = git.branchList().call();
        for (Ref ref : localBranches) {
            String name = ref.getName();
            if (name.startsWith(Constants.R_HEADS)) {
                name = name.substring(Constants.R_HEADS.length());
                String lastCommitId = ref.getObjectId().getName();
                String lastCommitMessage = getCommitMessage(lastCommitId);
                Branch branch = new Branch(name, lastCommitId, lastCommitMessage, false);
                if (name.equals(currentBranch)) {
                    branch.setCurrent(true);
                }
                branches.add(branch);
            }
        }

        // 获取远程分支
        List<Ref> remoteBranches = git.branchList().setListMode(ListBranchCommand.ListMode.REMOTE).call();
        for (Ref ref : remoteBranches) {
            String name = ref.getName();
            if (name.startsWith(Constants.R_REMOTES)) {
                name = name.substring(Constants.R_REMOTES.length());
                String lastCommitId = ref.getObjectId().getName();
                String lastCommitMessage = getCommitMessage(lastCommitId);
                Branch branch = new Branch(name, lastCommitId, lastCommitMessage, true);
                branches.add(branch);
            }
        }

        return branches;
    }

    /**
     * 获取提交信息
     * @param commitId 提交ID
     * @return 提交信息
     */
    private String getCommitMessage(String commitId) {
        try {
            ObjectId id = ObjectId.fromString(commitId);
            try (RevWalk revWalk = new RevWalk(git.getRepository())) {
                RevCommit commit = revWalk.parseCommit(id);
                return commit.getShortMessage();
            }
        } catch (Exception e) {
            return "";
        }
    }

    /**
     * 创建新分支
     * @param branchName 分支名称
     * @throws GitAPIException 如果创建分支失败
     */
    public void createBranch(String branchName) throws GitAPIException {
        git.branchCreate().setName(branchName).call();
    }

    /**
     * 切换分支
     * @param branchName 分支名称
     * @throws GitAPIException 如果切换分支失败
     */
    public void checkoutBranch(String branchName) throws GitAPIException {
        git.checkout().setName(branchName).call();
        updateCurrentBranch();
    }

    /**
     * 获取提交历史
     * @return 提交列表
     * @throws GitAPIException 如果获取提交历史失败
     */
    public List<Commit> getCommitHistory() throws GitAPIException {
        List<Commit> commits = new ArrayList<>();
        Iterable<RevCommit> revCommits = git.log().call();

        for (RevCommit revCommit : revCommits) {
            PersonIdent authorIdent = revCommit.getAuthorIdent();
            String id = revCommit.getName();
            String shortId = revCommit.getName().substring(0, 7);
            String message = revCommit.getFullMessage();
            String author = authorIdent.getName() + " <" + authorIdent.getEmailAddress() + ">";
            Date date = authorIdent.getWhen();

            Commit commit = new Commit(id, shortId, message, author, date);
            commits.add(commit);
        }

        return commits;
    }

    /**
     * 获取提交历史
     * @return 提交列表
     * @throws GitAPIException 如果获取提交历史失败
     * @throws IOException 如果操作过程中发生IO异常
     */
    public List<Commit> getCommits() throws GitAPIException, IOException {
        return getCommitHistory();
    }

    /**
     * 获取文件状态
     * @return 文件列表
     * @throws GitAPIException 如果获取文件状态失败
     */
    public List<GitFile> getFileStatus() throws GitAPIException {
        List<GitFile> files = new ArrayList<>();
        Status status = git.status().call();

        // 已暂存的文件
        Set<String> added = status.getAdded();
        for (String path : added) {
            files.add(new GitFile(path, GitFile.Status.ADDED, true));
        }

        Set<String> changed = status.getChanged();
        for (String path : changed) {
            files.add(new GitFile(path, GitFile.Status.MODIFIED, true));
        }

        Set<String> removed = status.getRemoved();
        for (String path : removed) {
            files.add(new GitFile(path, GitFile.Status.DELETED, true));
        }

        // 未暂存的文件
        Set<String> modified = status.getModified();
        for (String path : modified) {
            files.add(new GitFile(path, GitFile.Status.MODIFIED, false));
        }

        Set<String> missing = status.getMissing();
        for (String path : missing) {
            files.add(new GitFile(path, GitFile.Status.DELETED, false));
        }

        // 未跟踪的文件
        Set<String> untracked = status.getUntracked();
        for (String path : untracked) {
            files.add(new GitFile(path, GitFile.Status.UNTRACKED, false));
        }

        return files;
    }

    /**
     * 添加文件到暂存区
     * @param filePath 文件路径
     * @throws GitAPIException 如果添加文件失败
     */
    public void addFile(String filePath) throws GitAPIException {
        git.add().addFilepattern(filePath).call();
    }

    /**
     * 添加所有文件到暂存区
     * @throws GitAPIException 如果添加文件失败
     */
    public void addAllFiles() throws GitAPIException {
        git.add().addFilepattern(".").call();
    }

    /**
     * 从暂存区移除文件
     * @param filePath 文件路径
     * @throws GitAPIException 如果移除文件失败
     */
    public void removeFile(String filePath) throws GitAPIException {
        git.reset().addPath(filePath).call();
    }

    /**
     * 提交更改
     * @param message 提交信息
     * @throws GitAPIException 如果提交失败
     */
    public void commit(String message) throws GitAPIException {
        git.commit().setMessage(message).call();
    }

    /**
     * 推送更改到远程仓库
     * @param username 用户名
     * @param password 密码
     * @throws GitAPIException 如果推送失败
     */
    public void push(String username, String password) throws GitAPIException {
        CredentialsProvider credentialsProvider = new UsernamePasswordCredentialsProvider(username, password);
        git.push().setCredentialsProvider(credentialsProvider).call();
    }

    /**
     * 从远程仓库拉取更改
     * @param username 用户名
     * @param password 密码
     * @throws GitAPIException 如果拉取失败
     */
    public void pull(String username, String password) throws GitAPIException {
        CredentialsProvider credentialsProvider = new UsernamePasswordCredentialsProvider(username, password);
        git.pull().setCredentialsProvider(credentialsProvider).call();
    }

    /**
     * 获取仓库信息
     * @return 仓库信息
     */
    public Repository getRepository() {
        return repository;
    }

    /**
     * 获取文件状态列表
     * @return 文件状态列表
     * @throws GitAPIException 如果获取状态失败
     */
    public List<GitFile> getStatus() throws GitAPIException {
        return getFileStatus();
    }

    /**
     * 将文件添加到暂存区
     * @param filePath 文件路径
     * @throws GitAPIException 如果添加文件失败
     */
    public void stageFile(String filePath) throws GitAPIException {
        addFile(filePath);
    }

    /**
     * 从暂存区移除文件
     * @param filePath 文件路径
     * @throws GitAPIException 如果移除文件失败
     */
    public void unstageFile(String filePath) throws GitAPIException {
        removeFile(filePath);
    }

    /**
     * 将所有文件添加到暂存区
     * @throws GitAPIException 如果添加文件失败
     */
    public void stageAll() throws GitAPIException {
        addAllFiles();
    }

    /**
     * 从暂存区移除所有文件
     * @throws GitAPIException 如果移除文件失败
     */
    public void unstageAll() throws GitAPIException {
        git.reset().call();
    }
}